package prototype;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;


public class NetworkController implements Observer {

    //lista a halozatban levo kapukrol
    private  ArrayList<DigitalGate> digitalGates = new ArrayList<DigitalGate>();

    //halozatban levo vezetekek
    private  ArrayList<Wire> wireList = new ArrayList<Wire>();

    //halozatban levo kapcoslok
    private  ArrayList<Switch> switchList = new ArrayList<Switch>();

    //halozatban levo generatorok, kompozitokban levok is itt vannak
    private  ArrayList<Generator> generatorList = new ArrayList<Generator>();

    //halozatban levo oscilloszkopok
    private ArrayList<Oscilloscope> oscilloscopeList = new ArrayList<Oscilloscope>();

    //stabilitas index, hol tartunk?
    private int stableIndex;

    //stabilitas vizsgalat hany korben tortenjen
    private int stableCheckMax = 50;

    //statikus singleton class
    private static NetworkController instance = null;

    //stabilitas jelzo
    private boolean stable = true;

    //topo order valtozok
    private ArrayList<DigitalGate> topoOrderedGateList;
    private ArrayList<DigitalGate> depth;
    private ArrayList<DigitalGate> ended;

    //privat konstruktor
    private NetworkController()
    {

    }
    
    //singleton
    public static NetworkController getInstance()
    {
        //ha nincs meg egy ilyen class
        if (instance == null)
        {
            //akkor csinalunk egyet
            instance = new NetworkController();
        }
        //visszaadjuk
        return instance;
    }

    //wirelist getter
    public ArrayList<Wire> getWires(){ return wireList; }

    //gatelist getter
    public ArrayList<DigitalGate> getGates(){ return digitalGates; }
    //gatelist setter
    public void setGates(ArrayList<DigitalGate> value){ digitalGates = value; }

    //kapu hozzaadas
    public  void addGate (DigitalGate gate) {
        digitalGates.add(gate);
    }

    //oscilloszkop hozaaadas
    public  void addOscilloscope (Oscilloscope osc) {
        oscilloscopeList.add(osc);
    }

    //vezetek hozzaadas
    public  void addWire (Wire wire) {
        wireList.add(wire);
    }

    //generator hozaaadas
    public  void addGenerator (Generator generator) {
        generatorList.add(generator);
    }

    //kapcsolo hozaaadas
    public  void addSwitch (Switch myswitch) {
        switchList.add(myswitch);
    }

    //halozat leptetesenel hivodik
    public void Update () throws Exception {
        
        //Call Update several times for stability
        for(stableIndex = 0; stableIndex < stableCheckMax; stableIndex++){
        //Update network, call GateLogics
            for(int j = 0; j < digitalGates.size(); j++){
                digitalGates.get(j).gateLogic();
            }
        }
        //ha nem stabil akkor exception
        if (!stable)
        {
            throw new Exception("Unstable network");
        }

    }

    public  void stepGenerators () throws Exception {

        //Step generators
        for (Generator generator : generatorList) {
            generator.stepState();
        }

    }

    public  void stepOscilloscopes() throws Exception {

        //Step oscilloscopes
        for (Oscilloscope oscilloscope : oscilloscopeList) {
            oscilloscope.getState();
        }

    }

    public  ArrayList<DigitalGate> DoTopoOrder (ArrayList<DigitalGate> gateList) {
      
        topoOrderedGateList = new ArrayList<DigitalGate>();
        depth = new ArrayList<DigitalGate>();
        ended = new ArrayList<DigitalGate>();

        //több ág lehet így többször is kellhet eresni
        while(ended.size() != gateList.size()){
            DigitalGate tempGate = null;
            //forrás keresése, ha van :)
            for(DigitalGate gate : gateList){
                if(!ended.contains(gate)){
                    boolean source = true;
                    if(source){
                        for(Wire wire : gate.GetAllInput()){
                            if(wire.GetOutElement().getClass() == DigitalGate.class){
                                source = false;
                                break;
                            }
                        }
                    }
                    if(source){
                        tempGate = gate;
                        break;
                    }                    
                }
            }
            //Ha van forrás, akkor keresés
            if(tempGate != null){
                SearchTopoOrder(tempGate);
            }
            else{
                for(DigitalGate gate : gateList){
                    if(!ended.contains(gate)){
                        SearchTopoOrder(gate);
                        break;
                    }
                }
            }
        }

        //A befejezettek visszafelé sorrendben bekerülnek a topologikusan rendezett listába
        for(int i = ended.size() - 1; i >= 0; i--){
            topoOrderedGateList.add(ended.get(i));
        }

        return topoOrderedGateList;
    }

    private void SearchTopoOrder(DigitalGate gate){        
        boolean isEnded = true;
        
        //Ha nem voltunk még itt, akkor hozáadjuk a depth listához
        if(!depth.contains(gate)) depth.add(gate);

        for(Wire wire : gate.GetAllOutput()){
            //Ha nem jártunk még a hozzácsatolt gate-ben, akkor odamegyünk, Visszamenő élekkel nem foglalkozunk (Visszacsatolás)
            if(!depth.contains(wire.GetInGate())){
                isEnded = false;
                SearchTopoOrder(wire.GetInGate());
            }
        }
        
        //Ha nincs már több bejáratlan kimeneti gate, akkor befejeztük
        if(isEnded){
            ended.add(gate);
        }
    }
            

    //Observer method
    @Override
    public void update(Observable o, Object arg){

        //ha mar kozel vagyunk a vegehez es meg mindig volt valtozas akkor nem stabil
        if (stableIndex == stableCheckMax - 1){
            /*ErrorController.signalError("The Network is not stable on"
                    + stableCheckMax + " checking scale!");*/
            stable = false;
        }
    }

    public  void resetNetwork () throws Exception {

        //Reset generators
        for (Generator generator : generatorList) {
            generator.reset();
        }

        //Reset wires
        for(Wire wire : wireList){
            wire.reset();
        }

        //reset oscilloscopes
        for (Oscilloscope oscilloscope : oscilloscopeList) {
            oscilloscope.reset();
        }

    }

}

